<?php
/**
 * Created by PhpStorm.
 * User: zhonghuanglin
 * Date: 01/09/2017
 * Time: 15:28
 */

namespace inframe\library;

use inframe\Component;
use inframe\Exception;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\WebSocket\Frame;
use Swoole\Http\Server as HttpServer;
use Swoole\WebSocket\Server as WebsocketServer;
use Swoole\Http\Request as HttpRequest;

/**
 * Class Swooler swoole扩展类
 * @package inframe\library
 */
class Swooler extends Component
{
    /**
     * 默认配置
     * @var array
     */
    protected $_config = [];


    public function __construct(array $params = [], $name = 0)
    {
        if (!IN_IS_CLI) throw new Exception('swoole should be run under the cli-mode');
        parent::__construct($params, $name);
    }

    /**
     * @var [][]
     */
    private $wsservers = [];

    /**
     * 创建简单的websocket服务端
     * @param int $port 监听端口
     * @return WebsocketServer
     */
    public function createWebsocketServer($port)
    {
        if (!isset($this->wsservers[$port])) {

            $this->wsservers[$port] = [
                'sserver' => new WebsocketServer("0.0.0.0", $port),
                'idmap' => [],
                'fdmap' => [],
            ];


            $server = &$this->wsservers[$port];

            /** @var WebsocketServer $sserver */
            $sserver = &$server['sserver'];
            /** @var [] $idmap */
            $idmap = &$server['idmap'];
            /** @var [] $fdmap */
            $fdmap = [];

            $sserver->on('Start', [$this, 'onWebsocketStart']);

            # 可以调用push向客户端发送数据或者调用close关闭连接
            $sserver->on('open', function (WebsocketServer $server, HttpRequest $request) {
                $server->push($request->fd, "client {$request->fd} connect");
            });

            $sserver->on('message', function (WebsocketServer $server, Frame $frame) use ($idmap, $fdmap) {
                $fd = $frame->fd;

                $data = json_decode($frame->data, true);
                $code = 0;
                if (null === $data) {
                    # 无法解析JSON
                    $msg = 'invalid message : ' . $frame->data;
                    $code = 10000;
                } else {
                    if (isset($data['action'])) {
                        switch (isset($data['action']) ? $data['action'] : '') {
                            case 'sign':
                                # 必要参数
                                $identify = isset($data['identify']) ? $data['identify'] : '';
                                $token = isset($data['token']) ? $data['token'] : '';
                                # 处理流程
                                if (!$identify) {
                                    $msg = 'identify empty';
                                    $code = 10001;
                                } elseif (!$this->authorize($identify, $token)) {
                                    $msg = 'authorized failed';
                                    $code = 10002;
                                } else {
                                    $idmap[$identify] = $fd;
                                    $fdmap[$fd] = $identify;
                                    $msg = 'sign success';
                                }
                                break;
                            case 'transmit':
                                # 必要参数
                                if ($receiver = isset($data['receiver']) ? $data['receiver'] : '') {
                                    if (!isset($idmap[$receiver])) {
                                        $msg = 'transmit receiver unsigne';
                                        $code = 10006;
                                    } else {
                                        $receiver_fd = $idmap[$receiver];
                                        $server->push($receiver_fd, $frame->data);
                                        $msg = 'transmit success';
                                    }
                                } else {
                                    $msg = 'transmit require receiver';
                                    $code = 10005;
                                }
                                break;
                            default:
                                $msg = 'invalid message : ' . $frame->data;
                                $code = 10004;
                        }
                    } else {
                        $msg = 'message require function';
                        $code = 10007;
                    }
                }
                $server->push($fd, json_encode([
                    'code' => $code,
                    'msg' => $msg,
                ]));
            });

            $sserver->on('close', function (WebsocketServer $server, $fd) use ($idmap, $fdmap) {
                echo "client [{$fd}] closed\n";
                $identify = $idmap[$fd];
                unset($idmap[$identify]);
                unset($fdmap[$fd]);
            });
        }
        return $this->wsservers[$port];
    }

    protected function onWebsocketStart(Server $server)
    {
        $http = new HttpServer("127.0.0.1", 9501);
        $http->on('request',
            function ($request,
                /** @var Request $response */
                      $response) {
                $response->end("<h1>Hello Swoole. #" . rand(1000, 9999) . "</h1>");
            });
        $http->start();
    }

    public function authorize($identify, $token)
    {
        //TODO:验证标识符和token
        return $identify === $token;
    }

}